﻿#region zh-CHS 2006 - 2008 DemoSoft 团队 | en 2006 - 2008 DemoSoft Team

//     NOTES
// ---------------
//
// This file is a part of the MMOSE(Massively Multiplayer Online Server Engine) for .NET.
//
//                              2006-2008 DemoSoft Team
//
//
// First Version : by H.Q.Cai - mailto:caihuanqing@hotmail.com

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

#region zh-CHS 包含名字空间 | en Include namespace
using System;
using System.Reflection;
using System.Reflection.Emit;
#endregion

namespace Demo.Mmose.Core.Util
{
    /// <summary>
    /// Class with helper methods for dynamic invocation generating IL on the fly.
    /// </summary>
    public static class DynamicCalls
    {
        #region zh-CHS 共有静态方法 | en Public Static Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        public static FastInvokeHandler GetMethodInvoker( MethodInfo methodInfo )
        {
            // generates a dynamic method to generate a FastInvokeHandler delegate
            DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, typeof( object ), new Type[] { typeof( object ), typeof( object[] ) }, methodInfo.DeclaringType.Module );

            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

            ParameterInfo[] parameters = methodInfo.GetParameters();

            Type[] paramTypes = new Type[parameters.Length];

            // copies the parameter types to an array
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                if ( parameters[i].ParameterType.IsByRef )
                    paramTypes[i] = parameters[i].ParameterType.GetElementType();
                else
                    paramTypes[i] = parameters[i].ParameterType;
            }

            LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];

            // generates a local variable for each parameter
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                locals[i] = ilGenerator.DeclareLocal( paramTypes[i], true );
            }

            // creates code to copy the parameters to the local variables
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                ilGenerator.Emit( OpCodes.Ldarg_1 );
                EmitFastInt( ilGenerator, i );
                ilGenerator.Emit( OpCodes.Ldelem_Ref );
                EmitCastToReference( ilGenerator, paramTypes[i] );
                ilGenerator.Emit( OpCodes.Stloc, locals[i] );
            }

            if ( !methodInfo.IsStatic )
            {
                // loads the object into the stack
                ilGenerator.Emit( OpCodes.Ldarg_0 );
            }

            // loads the parameters copied to the local variables into the stack
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                if ( parameters[i].ParameterType.IsByRef )
                    ilGenerator.Emit( OpCodes.Ldloca_S, locals[i] );
                else
                    ilGenerator.Emit( OpCodes.Ldloc, locals[i] );
            }

            // calls the method
            if ( !methodInfo.IsStatic )
            {
                ilGenerator.EmitCall( OpCodes.Callvirt, methodInfo, null );
            }
            else
            {
                ilGenerator.EmitCall( OpCodes.Call, methodInfo, null );
            }

            // creates code for handling the return value
            if ( methodInfo.ReturnType == typeof( void ) )
            {
                ilGenerator.Emit( OpCodes.Ldnull );
            }
            else
            {
                EmitBoxIfNeeded( ilGenerator, methodInfo.ReturnType );
            }

            // iterates through the parameters updating the parameters passed by ref
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                if ( parameters[i].ParameterType.IsByRef )
                {
                    ilGenerator.Emit( OpCodes.Ldarg_1 );
                    EmitFastInt( ilGenerator, i );
                    ilGenerator.Emit( OpCodes.Ldloc, locals[i] );
                    if ( locals[i].LocalType.IsValueType )
                        ilGenerator.Emit( OpCodes.Box, locals[i].LocalType );
                    ilGenerator.Emit( OpCodes.Stelem_Ref );
                }
            }

            // returns the value to the caller
            ilGenerator.Emit( OpCodes.Ret );

            // converts the DynamicMethod to a FastInvokeHandler delegate to call to the method
            FastInvokeHandler invoker = (FastInvokeHandler)dynamicMethod.CreateDelegate( typeof( FastInvokeHandler ) );

            return invoker;
        }

        /// <summary>
        /// Gets the instance creator delegate that can be use to create instances of the specified type.
        /// </summary>
        /// <param name="type">The type of the objects we want to create.</param>
        /// <returns>A delegate that can be used to create the objects.</returns>
        public static FastCreateInstanceHandler GetInstanceCreator( Type type )
        {
            // generates a dynamic method to generate a FastCreateInstanceHandler delegate
            DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, type, new Type[0], typeof( DynamicCalls ).Module );

            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

            // generates code to create a new object of the specified type using the default constructor
            ilGenerator.Emit( OpCodes.Newobj, type.GetConstructor( Type.EmptyTypes ) );

            // returns the value to the caller
            ilGenerator.Emit( OpCodes.Ret );

            // converts the DynamicMethod to a FastCreateInstanceHandler delegate to create the object
            FastCreateInstanceHandler creator = (FastCreateInstanceHandler)dynamicMethod.CreateDelegate( typeof( FastCreateInstanceHandler ) );

            return creator;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propInfo"></param>
        /// <returns></returns>
        public static FastPropertyGetHandler GetPropertyGetter( PropertyInfo propInfo )
        {
            // generates a dynamic method to generate a FastPropertyGetHandler delegate
            DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, typeof( object ), new Type[] { typeof( object ) }, propInfo.DeclaringType.Module );

            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

            // loads the object into the stack
            ilGenerator.Emit( OpCodes.Ldarg_0 );

            // calls the getter
            ilGenerator.EmitCall( OpCodes.Callvirt, propInfo.GetGetMethod(), null );

            // creates code for handling the return value
            EmitBoxIfNeeded( ilGenerator, propInfo.PropertyType );

            // returns the value to the caller
            ilGenerator.Emit( OpCodes.Ret );

            // converts the DynamicMethod to a FastPropertyGetHandler delegate to get the property
            FastPropertyGetHandler getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate( typeof( FastPropertyGetHandler ) );

            return getter;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propInfo"></param>
        /// <returns></returns>
        public static FastPropertySetHandler GetPropertySetter( PropertyInfo propInfo )
        {
            // generates a dynamic method to generate a FastPropertySetHandler delegate
            DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, null, new Type[] { typeof( object ), typeof( object ) }, propInfo.DeclaringType.Module );

            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

            // loads the object into the stack
            ilGenerator.Emit( OpCodes.Ldarg_0 );

            // loads the parameter from the stack
            ilGenerator.Emit( OpCodes.Ldarg_1 );

            // cast to the proper type (unboxing if needed)
            EmitCastToReference( ilGenerator, propInfo.PropertyType );

            // calls the setter
            ilGenerator.EmitCall( OpCodes.Callvirt, propInfo.GetSetMethod(), null );

            // terminates the call
            ilGenerator.Emit( OpCodes.Ret );

            // converts the DynamicMethod to a FastPropertyGetHandler delegate to get the property
            FastPropertySetHandler setter = (FastPropertySetHandler)dynamicMethod.CreateDelegate( typeof( FastPropertySetHandler ) );

            return setter;
        }
        #endregion

        #region zh-CHS 私有静态方法 | en Private Static Methods
        /// <summary>
        /// Emits the cast to a reference, unboxing if needed.
        /// </summary>
        /// <param name="il">The MSIL generator.</param>
        /// <param name="type">The type to cast.</param>
        private static void EmitCastToReference( ILGenerator ilGenerator, System.Type type )
        {
            if ( type.IsValueType )
            {
                ilGenerator.Emit( OpCodes.Unbox_Any, type );
            }
            else
            {
                ilGenerator.Emit( OpCodes.Castclass, type );
            }
        }

        /// <summary>
        /// Boxes a type if needed.
        /// </summary>
        /// <param name="ilGenerator">The MSIL generator.</param>
        /// <param name="type">The type.</param>
        private static void EmitBoxIfNeeded( ILGenerator ilGenerator, System.Type type )
        {
            if ( type.IsValueType )
            {
                ilGenerator.Emit( OpCodes.Box, type );
            }
        }

        /// <summary>
        /// Emits code to save an integer to the evaluation stack.
        /// </summary>
        /// <param name="ilGeneartor">The MSIL generator.</param>
        /// <param name="value">The value to push.</param>
        private static void EmitFastInt( ILGenerator ilGenerator, int value )
        {
            // for small integers, emit the proper opcode
            switch ( value )
            {
                case -1:
                    ilGenerator.Emit( OpCodes.Ldc_I4_M1 );
                    return;
                case 0:
                    ilGenerator.Emit( OpCodes.Ldc_I4_0 );
                    return;
                case 1:
                    ilGenerator.Emit( OpCodes.Ldc_I4_1 );
                    return;
                case 2:
                    ilGenerator.Emit( OpCodes.Ldc_I4_2 );
                    return;
                case 3:
                    ilGenerator.Emit( OpCodes.Ldc_I4_3 );
                    return;
                case 4:
                    ilGenerator.Emit( OpCodes.Ldc_I4_4 );
                    return;
                case 5:
                    ilGenerator.Emit( OpCodes.Ldc_I4_5 );
                    return;
                case 6:
                    ilGenerator.Emit( OpCodes.Ldc_I4_6 );
                    return;
                case 7:
                    ilGenerator.Emit( OpCodes.Ldc_I4_7 );
                    return;
                case 8:
                    ilGenerator.Emit( OpCodes.Ldc_I4_8 );
                    return;
            }

            // for bigger values emit the short or long opcode
            if ( value > -129 && value < 128 )
            {
                ilGenerator.Emit( OpCodes.Ldc_I4_S, (SByte)value );
            }
            else
            {
                ilGenerator.Emit( OpCodes.Ldc_I4, value );
            }
        }
        #endregion
    }
}
#endregion